﻿#region Copyright (c) 11/24/2011, Olaf Kober <amarok.projects@gmail.com>
//================================================================================
//	Permission is hereby granted, free of charge, to any person obtaining a copy
//	of this software and associated documentation files (the "Software"), to deal
//	in the Software without restriction, including without limitation the rights
//	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//	copies of the Software, and to permit persons to whom the Software is
//	furnished to do so, subject to the following conditions:
//
//	The above copyright notice and this permission notice shall be included in
//	all copies or substantial portions of the Software.
//
//	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//	THE SOFTWARE.
//================================================================================
#endregion

#if TESTS

using System;
using System.ComponentModel;
using NUnit.Framework;


namespace Amarok.Presentation
{
	[TestFixture]
	public class Test_ViewModel
	{
		private class MyViewModel : ViewModel
		{
			public String MyProperty
			{
				get
				{
					return "BLAH";
				}
				set
				{
					base.RaisePropertyChanged(() => MyProperty);
				}
			}

			public String MyOtherProperty
			{
				get
				{
					return "BLAH";
				}
				set
				{
					base.RaisePropertyChanged(() => MyOtherProperty);
				}
			}

			public String MyThirdProperty
			{
				get
				{
					return "BLAH";
				}
				set
				{
					base.RaisePropertyChanged();
				}
			}

			private String mText;

			public String Text
			{
				get
				{
					return mText;
				}
				set
				{
					base.SetProperty(ref mText, value);
				}
			}

			private Int32 mValue;

			public Int32 Value
			{
				get
				{
					return mValue;
				}
				set
				{
					base.SetProperty(ref mValue, value, forceOverwrite: true);
				}
			}

			private String mText2;

			public String Text2
			{
				get
				{
					return mText2;
				}
				set
				{
					base.SetProperty(() => Text2, ref mText2, value);
				}
			}

			private Int32 mValue2;

			public Int32 Value2
			{
				get
				{
					return mValue2;
				}
				set
				{
					base.SetProperty(() => Value2, ref mValue2, value, true);
				}
			}

			public Int32 NotExistingValue
			{
				get
				{
					return mValue2;
				}
				set
				{
					base.SetProperty(ref mValue2, value, "NotExistingProperty");
				}
			}

			public MyViewModel()
			{
			}

			public MyViewModel(MyViewModel parent)
				: base(parent)
			{
			}
		}


		// --- TESTS ---


		[Test]
		public void Test_RaisePropertyChanged_Expression()
		{
			var viewModel = new MyViewModel();

			var handlerCalled = 0;
			var handlerPropertyName = String.Empty;

			viewModel.PropertyChanged += (sender, e) =>
				{
					handlerPropertyName = e.PropertyName;
					handlerCalled++;
				};

			viewModel.RaisePropertyChanged(() => viewModel.MyProperty);

			Assert.That(handlerCalled, Is.EqualTo(1));
			Assert.That(handlerPropertyName, Is.EqualTo("MyProperty"));
		}

		[Test]
		public void Test_RaisePropertyChanged_PropertyName()
		{
			var viewModel = new MyViewModel();

			var handlerCalled = 0;
			var handlerPropertyName = String.Empty;

			viewModel.PropertyChanged += (sender, e) =>
				{
					handlerPropertyName = e.PropertyName;
					handlerCalled++;
				};

			viewModel.RaisePropertyChanged("MyProperty");

			Assert.That(handlerCalled, Is.EqualTo(1));
			Assert.That(handlerPropertyName, Is.EqualTo("MyProperty"));
		}

		[Test]
		public void Test_RaisePropertyChanged_PropertyName_NotExisting()
		{
			var viewModel = new MyViewModel();

			var handlerCalled = 0;

			viewModel.PropertyChanged += (sender, e) =>
				{
					handlerCalled++;
				};

			Assert.Throws<ArgumentException>(() =>
				viewModel.RaisePropertyChanged("NotExistingProperty"));

			Assert.That(handlerCalled, Is.EqualTo(0));
		}

		[Test]
		public void Test_RaisePropertyChanged_PropertyName_Default()
		{
			var viewModel = new MyViewModel();

			var handlerCalled = 0;
			var handlerPropertyName = String.Empty;

			viewModel.PropertyChanged += (sender, e) =>
			{
				handlerPropertyName = e.PropertyName;
				handlerCalled++;
			};

			viewModel.MyThirdProperty = "TEST";

			Assert.That(handlerCalled, Is.EqualTo(1));
			Assert.That(handlerPropertyName, Is.EqualTo("MyThirdProperty"));

			viewModel.RaisePropertyChanged("MyProperty");

			Assert.That(handlerCalled, Is.EqualTo(2));
			Assert.That(handlerPropertyName, Is.EqualTo("MyProperty"));
		}

		[Test]
		public void Test_RaisePropertiesChanged()
		{
			var viewModel = new MyViewModel();

			var handlerCalled = 0;
			var handlerPropertyName = String.Empty;

			viewModel.PropertyChanged += (sender, e) =>
				{
					handlerPropertyName = e.PropertyName;
					handlerCalled++;
				};

			viewModel.RaisePropertiesChanged();

			Assert.That(handlerCalled, Is.EqualTo(1));
			Assert.That(handlerPropertyName, Is.EqualTo(null));
		}

		[Test]
		public void Test_RaisePropertyChanged_BeginEndUpdates_Event()
		{
			var viewModel = new MyViewModel();

			var handlerCalled = 0;
			var handlerPropertyName = String.Empty;

			viewModel.PropertyChanged += (sender, e) =>
				{
					handlerPropertyName = e.PropertyName;
					handlerCalled++;
				};

			viewModel.BeginUpdate();
			viewModel.RaisePropertiesChanged();
			viewModel.RaisePropertyChanged(() => viewModel.MyProperty);
			viewModel.RaisePropertyChanged("MyProperty");
			viewModel.EndUpdate();

			Assert.That(handlerCalled, Is.EqualTo(0));
			Assert.That(handlerPropertyName, Is.EqualTo(String.Empty));

			viewModel.RaisePropertyChanged(() => viewModel.MyProperty);

			Assert.That(handlerCalled, Is.EqualTo(1));
			Assert.That(handlerPropertyName, Is.EqualTo("MyProperty"));
		}

		[Test]
		public void Test_RaisePropertyChanged_RegisterForPropertyChange_Expression()
		{
			var viewModel = new MyViewModel();

			var handlerCalled = 0;
			var handlerPropertyName = String.Empty;

			var disposable = viewModel.RegisterForPropertyChange(() => viewModel.MyProperty, (sender, e) =>
				{
					handlerPropertyName = e.PropertyName;
					handlerCalled++;
				});

			viewModel.RaisePropertyChanged(() => viewModel.MyProperty);

			Assert.That(handlerCalled, Is.EqualTo(1));
			Assert.That(handlerPropertyName, Is.EqualTo("MyProperty"));

			handlerCalled = 0;
			handlerPropertyName = String.Empty;

			disposable.Dispose();
			viewModel.RaisePropertyChanged(() => viewModel.MyProperty);

			Assert.That(handlerCalled, Is.EqualTo(0));
		}

		[Test]
		public void Test_RaisePropertyChanged_RegisterForPropertyChange_PropertyName()
		{
			var viewModel = new MyViewModel();

			var handlerCalled = 0;
			var handlerPropertyName = String.Empty;

			var disposable = viewModel.RegisterForPropertyChange("MyProperty", (sender, e) =>
				{
					handlerPropertyName = e.PropertyName;
					handlerCalled++;
				});

			viewModel.RaisePropertyChanged(() => viewModel.MyProperty);

			Assert.That(handlerCalled, Is.EqualTo(1));
			Assert.That(handlerPropertyName, Is.EqualTo("MyProperty"));

			handlerCalled = 0;
			handlerPropertyName = String.Empty;

			disposable.Dispose();
			viewModel.RaisePropertyChanged(() => viewModel.MyProperty);

			Assert.That(handlerCalled, Is.EqualTo(0));
		}

		[Test]
		public void Test_RaisePropertiesChanged_RegisterForPropertyChange()
		{
			var viewModel = new MyViewModel();

			var handlerCalled = 0;
			var handlerPropertyName = String.Empty;

			viewModel.RegisterForPropertyChange("MyProperty", (sender, e) =>
				{
					handlerPropertyName = e.PropertyName;
					handlerCalled++;
				});

			viewModel.RaisePropertiesChanged();

			Assert.That(handlerCalled, Is.EqualTo(1));
			Assert.That(handlerPropertyName, Is.EqualTo(null));
		}

		[Test]
		public void Test_RaisePropertyChanged_RegisterForPropertyChange_PropertyName_NotExisting()
		{
			var viewModel = new MyViewModel();

			var handlerCalled = 0;

			Assert.Throws<ArgumentException>(() =>
				{
					viewModel.RegisterForPropertyChange("NotExistingProperty", (sender, e) =>
						{
							handlerCalled++;
						});
				});

			viewModel.RaisePropertyChanged(() => viewModel.MyProperty);

			Assert.That(handlerCalled, Is.EqualTo(0));
		}

		[Test]
		public void Test_RaisePropertyChanged_RegisterForPropertyChange_BeginEndUpdate()
		{
			var viewModel = new MyViewModel();

			var handlerCalled = 0;
			var handlerPropertyName = String.Empty;

			viewModel.RegisterForPropertyChange("MyProperty", (sender, e) =>
				{
					handlerPropertyName = e.PropertyName;
					handlerCalled++;
				});

			viewModel.BeginUpdate();
			viewModel.RaisePropertiesChanged();
			viewModel.RaisePropertyChanged(() => viewModel.MyProperty);
			viewModel.RaisePropertyChanged("MyProperty");
			viewModel.EndUpdate();

			Assert.That(handlerCalled, Is.EqualTo(0));

			viewModel.RaisePropertyChanged(() => viewModel.MyProperty);

			Assert.That(handlerCalled, Is.EqualTo(1));
			Assert.That(handlerPropertyName, Is.EqualTo("MyProperty"));
		}

		[Test]
		public void Test_RaisePropertyChanged_RegisterForPropertyChange_MultipleHandlers()
		{
			var viewModel = new MyViewModel();

			var handler1Called = 0;
			var handler1PropertyName = String.Empty;
			var handler2Called = 0;
			var handler2PropertyName = String.Empty;

			var disposable1 = viewModel.RegisterForPropertyChange("MyProperty", (sender, e) =>
				{
					handler1PropertyName = e.PropertyName;
					handler1Called++;
				});

			var disposable2 = viewModel.RegisterForPropertyChange("MyProperty", (sender, e) =>
				{
					handler2PropertyName = e.PropertyName;
					handler2Called++;
				});

			viewModel.RaisePropertyChanged(() => viewModel.MyProperty);

			Assert.That(handler1Called, Is.EqualTo(1));
			Assert.That(handler1PropertyName, Is.EqualTo("MyProperty"));
			Assert.That(handler2Called, Is.EqualTo(1));
			Assert.That(handler2PropertyName, Is.EqualTo("MyProperty"));

			disposable1.Dispose();
			viewModel.RaisePropertyChanged(() => viewModel.MyProperty);
			viewModel.RaisePropertyChanged(() => viewModel.MyProperty);

			Assert.That(handler1Called, Is.EqualTo(1));
			Assert.That(handler1PropertyName, Is.EqualTo("MyProperty"));
			Assert.That(handler2Called, Is.EqualTo(3));
			Assert.That(handler2PropertyName, Is.EqualTo("MyProperty"));

			disposable2.Dispose();
			viewModel.RaisePropertyChanged(() => viewModel.MyProperty);
			viewModel.RaisePropertyChanged(() => viewModel.MyProperty);

			Assert.That(handler1Called, Is.EqualTo(1));
			Assert.That(handler1PropertyName, Is.EqualTo("MyProperty"));
			Assert.That(handler2Called, Is.EqualTo(3));
			Assert.That(handler2PropertyName, Is.EqualTo("MyProperty"));
		}

		[Test]
		public void Test_RaisePropertyChanged_RemoveEventHandler()
		{
			var viewModel = new MyViewModel();

			var handlerCalled = 0;
			var handlerPropertyName = String.Empty;

			PropertyChangedEventHandler handler = (sender, e) =>
				{
					handlerPropertyName = e.PropertyName;
					handlerCalled++;
				};

			viewModel.PropertyChanged += handler;
			viewModel.RaisePropertyChanged(() => viewModel.MyProperty);

			Assert.That(handlerCalled, Is.EqualTo(1));
			Assert.That(handlerPropertyName, Is.EqualTo("MyProperty"));

			viewModel.PropertyChanged -= handler;
			viewModel.RaisePropertyChanged(() => viewModel.MyProperty);

			Assert.That(handlerCalled, Is.EqualTo(1));
			Assert.That(handlerPropertyName, Is.EqualTo("MyProperty"));
		}

		[Test]
		public void Test_BeginEndRecording()
		{
			var viewModel = new MyViewModel();
			Assert.That(viewModel.GetRecording(), Is.Null);

			viewModel.BeginRecording();
			Assert.That(viewModel.GetRecording(), Is.Empty);

			viewModel.MyProperty = "BLAH";
			Assert.That(viewModel.GetRecording().AsString(";"), Is.EqualTo("MyProperty"));

			viewModel.MyOtherProperty = "BLAH";
			Assert.That(viewModel.GetRecording().AsString(";"), Is.EqualTo("MyProperty;MyOtherProperty"));

			viewModel.BeginRecording();
			Assert.That(viewModel.GetRecording(), Is.Empty);

			viewModel.MyOtherProperty = "BLAH";
			viewModel.MyProperty = "BLAH";
			Assert.That(viewModel.GetRecording().AsString(";"), Is.EqualTo("MyOtherProperty;MyProperty"));

			viewModel.EndRecording();
			viewModel.MyProperty = "BLAH";
			Assert.That(viewModel.GetRecording(), Is.Null);
		}

		[Test]
		public void Test_SetProperty_PropertyName_String()
		{
			var viewModel = new MyViewModel();

			var handlerCalled = 0;
			var handlerPropertyName = String.Empty;

			viewModel.PropertyChanged += (sender, e) =>
			{
				handlerPropertyName = e.PropertyName;
				handlerCalled++;
			};

			viewModel.Text = "AAA";

			Assert.That(handlerCalled, Is.EqualTo(1));
			Assert.That(handlerPropertyName, Is.EqualTo("Text"));
			Assert.That(viewModel.Text, Is.EqualTo("AAA"));

			viewModel.Text = "AAA";

			Assert.That(handlerCalled, Is.EqualTo(1));
			Assert.That(handlerPropertyName, Is.EqualTo("Text"));
			Assert.That(viewModel.Text, Is.EqualTo("AAA"));

			viewModel.Text = "BBB";

			Assert.That(handlerCalled, Is.EqualTo(2));
			Assert.That(handlerPropertyName, Is.EqualTo("Text"));
			Assert.That(viewModel.Text, Is.EqualTo("BBB"));
		}

		[Test]
		public void Test_SetProperty_PropertyName_Int32_ForceOverwriting()
		{
			var viewModel = new MyViewModel();

			var handlerCalled = 0;
			var handlerPropertyName = String.Empty;

			viewModel.PropertyChanged += (sender, e) =>
			{
				handlerPropertyName = e.PropertyName;
				handlerCalled++;
			};

			viewModel.Value = 111;

			Assert.That(handlerCalled, Is.EqualTo(1));
			Assert.That(handlerPropertyName, Is.EqualTo("Value"));
			Assert.That(viewModel.Value, Is.EqualTo(111));

			viewModel.Value = 111;

			Assert.That(handlerCalled, Is.EqualTo(2));
			Assert.That(handlerPropertyName, Is.EqualTo("Value"));
			Assert.That(viewModel.Value, Is.EqualTo(111));

			viewModel.Value = 222;

			Assert.That(handlerCalled, Is.EqualTo(3));
			Assert.That(handlerPropertyName, Is.EqualTo("Value"));
			Assert.That(viewModel.Value, Is.EqualTo(222));
		}

		[Test]
		public void Test_SetProperty_PropertyName_NotExistingPropertyName()
		{
			var viewModel = new MyViewModel();

			Assert.Throws<ArgumentException>(() =>
				viewModel.NotExistingValue = 123);
		}

		[Test]
		public void Test_SetProperty_PropertyExpression_String()
		{
			var viewModel = new MyViewModel();

			var handlerCalled = 0;
			var handlerPropertyName = String.Empty;

			viewModel.PropertyChanged += (sender, e) =>
			{
				handlerPropertyName = e.PropertyName;
				handlerCalled++;
			};

			viewModel.Text2 = "AAA";

			Assert.That(handlerCalled, Is.EqualTo(1));
			Assert.That(handlerPropertyName, Is.EqualTo("Text2"));
			Assert.That(viewModel.Text2, Is.EqualTo("AAA"));

			viewModel.Text2 = "AAA";

			Assert.That(handlerCalled, Is.EqualTo(1));
			Assert.That(handlerPropertyName, Is.EqualTo("Text2"));
			Assert.That(viewModel.Text2, Is.EqualTo("AAA"));

			viewModel.Text2 = "BBB";

			Assert.That(handlerCalled, Is.EqualTo(2));
			Assert.That(handlerPropertyName, Is.EqualTo("Text2"));
			Assert.That(viewModel.Text2, Is.EqualTo("BBB"));
		}

		[Test]
		public void Test_SetProperty_PropertyExpression_ForceOverwriting()
		{
			var viewModel = new MyViewModel();

			var handlerCalled = 0;
			var handlerPropertyName = String.Empty;

			viewModel.PropertyChanged += (sender, e) =>
			{
				handlerPropertyName = e.PropertyName;
				handlerCalled++;
			};

			viewModel.Value2 = 111;

			Assert.That(handlerCalled, Is.EqualTo(1));
			Assert.That(handlerPropertyName, Is.EqualTo("Value2"));
			Assert.That(viewModel.Value2, Is.EqualTo(111));

			viewModel.Value2 = 111;

			Assert.That(handlerCalled, Is.EqualTo(2));
			Assert.That(handlerPropertyName, Is.EqualTo("Value2"));
			Assert.That(viewModel.Value2, Is.EqualTo(111));

			viewModel.Value2 = 222;

			Assert.That(handlerCalled, Is.EqualTo(3));
			Assert.That(handlerPropertyName, Is.EqualTo("Value2"));
			Assert.That(viewModel.Value2, Is.EqualTo(222));
		}

		[Test]
		public void Test_Parent()
		{
			var root = new MyViewModel();
			var child = new MyViewModel(root);

			Assert.That(root.HasParent, Is.False);
			Assert.That(root.Parent, Is.Null);
			Assert.That(child.HasParent, Is.True);
			Assert.That(child.Parent, Is.SameAs(root));

			child.BeginRecording();
			child.Parent = null;

			Assert.That(child.GetRecording().AsString(";"), Is.EqualTo("Parent;HasParent"));
			Assert.That(child.HasParent, Is.False);
			Assert.That(child.Parent, Is.Null);

			child.BeginRecording();
			child.Parent = root;

			Assert.That(child.GetRecording().AsString(";"), Is.EqualTo("Parent;HasParent"));
			Assert.That(child.HasParent, Is.True);
			Assert.That(child.Parent, Is.SameAs(root));
		}

	}
}

#endif
